/*
 * Copyright (C) 2010-2011 Comcast Cable.  All Rights Reserved.
 */
package com.hercules.android.receiver.handlers.commands.call.animation;

import com.hercules.android.receiver.AndroidReceiver;
import com.hercules.android.receiver.ApplicationData;
import com.hercules.android.receiver.handlers.commands.XREViewFinderUtility;
import com.hercules.android.receiver.handlers.commands.XREViewFinderUtilityException;
import com.hercules.android.receiver.model.commands.XRECallCommand;
import com.hercules.android.receiver.model.commands.XRECommand;
import com.hercules.android.receiver.model.objects.XREObject;
import com.hercules.android.receiver.model.objects.XREView;

import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;

/**
 * This class represents a finder used to find all the views affected by the animation call. It implements
 * XREViewFinderUtility.
 * <p>
 * <b>Simple usage:</b>
 *
 * <pre>
 * // create instance
 * XREAnimationViewFinderUtility utility = new XREAnimationViewFinderUtility();
 *
 * // you can inject your own logger, this is optional
 * utility.setLogger(Logger.getLogger(&quot;logger&quot;));
 *
 * // set command and receiver to correct values
 * XRECallCommand command = COMMAND;
 * AndroidReceiver receiver = RECEIVER;
 *
 * try {
 *     // get affected views
 *     List&lt;XREView&gt; result = utility.getAffectedViews(command, receiver);
 *
 *     // result will contain parent of View, specified by command.targetId
 *     // if parent is absent(null) then empty list is returned
 *     result.size();
 * } catch (XREViewFinderUtilityException e) {
 *     // process exception
 *     e.printStackTrace();
 * }
 * </pre>
 * <p>
 * <b>Thread Safety:</b> This class is mutable because the logger field is mutable, it's not technically thread-safe. It
 * would be thread-safe if the logger field does not change after initialization.
 *
 * @author semi_sleep, 7realm
 * @version 1.0
 */
public class XREAnimationViewFinderUtility implements XREViewFinderUtility {
    /**
     * Represents the name of current class.
     * <p>
     * Initialized to simple name of the class at construction.
     */
    private final String className = getClass().getName();

    /**
     * The logger used to write log.
     * <p>
     * Can not be null. It's initialized in constructor with default logger name (current class name), it can be changed
     * by its public setter. It's used by the getAffectedViews() method, it has a public getter to expose it to external
     * codes.
     */
    private Logger logger;

    /**
     * Creates an instance of XREAnimationCallViewFinderUtility.
     */
    public XREAnimationViewFinderUtility() {
        logger = Logger.getLogger(this.getClass().getName());
    }

    /**
     * Retrieve the views affected by given command. This should return the parent view of the CALL command's target
     * view.
     *
     * @param receiver the AndroidReceiver object containing reference to application data to be used while processing
     * command message
     * @param command the XRECommand object which should be a call command to call the "animate" method on XREView
     * @return a list of affected views
     * @throws IllegalArgumentException if receiver or command is null, or if command is not XRECallCommand, or if
     * command.method is not "animate"
     * @throws XREViewFinderUtilityException if any error occurs
     */
    public List<XREView> getAffectedViews(XRECommand command, AndroidReceiver receiver)
        throws XREViewFinderUtilityException {
        String methodName = "getAffectedViews";
        logger.entering(className, methodName, new Object[] {command, receiver});

        // create a view list
        List<XREView> list = new ArrayList<XREView>();

        try {
            // check arguments
            Helper.checkNotNull(receiver, "receiver");
            XRECallCommand callCommand = Helper.checkCommand(command);

            // get application data
            ApplicationData appData = getAppData(receiver);

            // get XRE view
            XREView xreView = getXREView(callCommand, appData);
            if (xreView.getParent() != null) {
                list.add(xreView.getParent());
            }
            return list;
        } catch (IllegalArgumentException e) {
            throw Helper.logError(logger, className, methodName, e);
        } catch (XREViewFinderUtilityException e) {
            throw Helper.logError(logger, className, methodName, e);
        } finally {
            logger.exiting(className, methodName, list);
        }
    }

    /**
     * Gets the logger used to write log.
     *
     * @return the logger used to write log
     */
    public Logger getLogger() {
        return logger;
    }

    /**
     * Sets the logger used to write log.
     *
     * @param logger logger used to write log
     * @throws IllegalArgumentException if logger is null
     */
    public void setLogger(Logger logger) {
        // check arguments
        Helper.checkNotNull(logger, "logger");

        this.logger = logger;
    }

    /**
     * Gets application data from android receiver.
     *
     * @param receiver android receiver
     * @return application data, stored in receiver
     * @throws XREViewFinderUtilityException if application data is incorrect or not present
     */
    private static ApplicationData getAppData(AndroidReceiver receiver) throws XREViewFinderUtilityException {
        // get application data
        ApplicationData appData = receiver.getApplicationData();
        checkNotNull(appData, "receiver.applicationData");
        checkNotNull(appData.getElementsXREIdToObject(), "receiver.applicationData.elementsXREIdToObject");
        return appData;
    }

    /**
     * Gets XRE view from application data.
     *
     * @param callCommand the used call command
     * @param appData application data
     * @return required XRE view
     * @throws XREViewFinderUtilityException if XRE view is not present or has incorrect type
     */
    private static XREView getXREView(XRECallCommand callCommand, ApplicationData appData)
        throws XREViewFinderUtilityException {
        Integer targetId = Integer.valueOf(callCommand.getTargetId());
        String xreViewName = "receiver.applicationData.elementsXREIdToObject[" + targetId + "]";

        // get XRE view
        XREObject xreViewObj = appData.getElementsXREIdToObject().get(targetId);
        checkNotNull(xreViewObj, xreViewName);

        // check type of XRE view object
        if (!(xreViewObj instanceof XREView)) {
            throw new XREViewFinderUtilityException("Object '" + xreViewName + "' must be instance of XREView class.");
        }
        return (XREView) xreViewObj;
    }

    /**
     * Checks if given object is not null.
     *
     * @param object the object to check
     * @param name the name of checked object
     * @throws XREViewFinderUtilityException if object is null
     */
    private static void checkNotNull(Object object, String name) throws XREViewFinderUtilityException {
        if (object == null) {
            throw new XREViewFinderUtilityException("Value of '" + name + "' is null.");
        }
    }
}